package com.time.admin.domain.service.impl;

import com.time.admin.domain.entity.CacheKeyItemBO;
import com.time.admin.domain.entity.CacheMetricsBO;
import com.time.admin.domain.service.AdminCacheDomainService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 缓存管理领域服务层接口实现类
 *
 * @author: HuangQi
 * @date: Created in 2025/01/XX
 * @description: 缓存管理领域服务层接口实现
 */
@Service("adminCacheDomainService")
@Slf4j
public class AdminCacheDomainServiceImpl implements AdminCacheDomainService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public CacheMetricsBO metrics(String prefix) {
        if (log.isInfoEnabled()) {
            log.info("AdminCacheDomainServiceImpl.metrics.prefix:{}", prefix);
        }

        // 构建缓存统计对象
        CacheMetricsBO metrics = new CacheMetricsBO();
        metrics.setBackend("redis");

        // 获取Redis键数量
        try {
            Long dbSize = redisTemplate.execute((RedisCallback<Long>) RedisConnection::dbSize);
            metrics.setEntryCount(dbSize == null ? 0 : dbSize);
        } catch (Exception e) {
            log.warn("read redis dbsize fail: {}", e.getMessage());
        }

        // 获取Redis内存使用情况
        try {
            // 使用 INFO memory 估算 used_memory
            String info = redisTemplate.execute((RedisCallback<String>) connection -> {
                byte[] bytes = connection.info("memory");
                return bytes == null ? null : new String(bytes, StandardCharsets.UTF_8);
            });
            long used = parseUsedMemory(info);
            metrics.setSizeBytes(used);
        } catch (Exception e) {
            log.warn("read redis memory info fail: {}", e.getMessage());
        }

        // 返回结果
        return metrics;
    }

    @Override
    public List<CacheKeyItemBO> listKeys(String pattern, int page, int size) {
        if (log.isInfoEnabled()) {
            log.info("AdminCacheDomainServiceImpl.listKeys.pattern:{}, page:{}, size:{}", pattern, page, size);
        }

        // 构建扫描模式和分页参数
        String scanPattern = pattern == null || pattern.isEmpty() ? "*" : pattern;
        int offset = Math.max(0, page) * Math.max(1, size);
        int limit = Math.max(1, size);

        // 使用SCAN命令分页扫描Redis键
        return redisTemplate.execute((RedisCallback<List<CacheKeyItemBO>>) connection -> {
            List<CacheKeyItemBO> result = new ArrayList<>();
            ScanOptions options = ScanOptions.scanOptions().match(scanPattern).count(1000).build();
            try (Cursor<byte[]> cursor = connection.scan(options)) {
                int index = 0;
                while (cursor.hasNext() && result.size() < limit) {
                    byte[] k = cursor.next();
                    // 跳过偏移量之前的键
                    if (index++ < offset) {
                        continue;
                    }
                    String key = new String(k, StandardCharsets.UTF_8);
                    CacheKeyItemBO item = new CacheKeyItemBO();
                    item.setKey(key);
                    // 获取键的剩余过期时间
                    try {
                        Long pttl = connection.keyCommands().pTtl(k);
                        item.setTtlLeftSeconds(pttl == null || pttl < 0 ? null : pttl / 1000);
                    } catch (Exception ignore) {
                    }
                    // 获取键的内存占用
                    try {
                        Long usage = connection.keyCommands().memoryUsage(k);
                        item.setApproxSizeBytes(usage == null ? 0 : usage);
                    } catch (Exception ignore) {
                    }
                    result.add(item);
                }
            } catch (Exception e) {
                log.error("scan keys error", e);
            }
            return result;
        });
    }

    @Override
    public boolean evict(String key) {
        if (log.isInfoEnabled()) {
            log.info("AdminCacheDomainServiceImpl.evict.key:{}", key);
        }

        // 删除指定的缓存键
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    @Override
    public long evictByPattern(String pattern) {
        if (log.isInfoEnabled()) {
            log.info("AdminCacheDomainServiceImpl.evictByPattern.pattern:{}", pattern);
        }

        // 构建扫描模式
        String scanPattern = pattern == null || pattern.isEmpty() ? "*" : pattern;

        // 使用SCAN命令扫描匹配的键并批量删除
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            long count = 0;
            ScanOptions options = ScanOptions.scanOptions().match(scanPattern).count(1000).build();
            try (Cursor<byte[]> cursor = connection.scan(options)) {
                while (cursor.hasNext()) {
                    byte[] k = cursor.next();
                    try {
                        connection.keyCommands().del(k);
                        count++;
                    } catch (DataAccessException e) {
                        log.warn("delete key fail", e);
                    }
                }
            }
            return count;
        });
    }

    @Override
    public long evictAll(String prefix) {
        if (log.isInfoEnabled()) {
            log.info("AdminCacheDomainServiceImpl.evictAll.prefix:{}", prefix);
        }

        // 构建扫描模式，如果前缀为空则匹配所有键
        String pattern = (prefix == null || prefix.isEmpty()) ? "*" : (prefix + "*");

        // 调用按模式删除方法
        return evictByPattern(pattern);
    }

    /**
     * 解析Redis INFO memory命令返回的used_memory值
     *
     * @param info Redis INFO memory命令的返回结果
     * @return 内存使用量（字节）
     */
    private long parseUsedMemory(String info) {
        if (info == null) {
            return 0L;
        }
        for (String line : info.split("\n")) {
            if (line.startsWith("used_memory:")) {
                String v = line.substring("used_memory:".length()).trim();
                try {
                    return Long.parseLong(v);
                } catch (NumberFormatException ignore) {
                }
            }
        }
        return 0L;
    }
}


